<template>
    <div :style="{ height: height + 'px' }" class="relative !h-full w-full overflow-hidden" ref="el"></div>
</template>

<script lang="ts" setup>
import { ref, onMounted, onUnmounted, watchEffect, watch, unref, nextTick, PropType } from 'vue'
import { useDebounceFn } from '@vueuse/core'
import { useWindowSizeFn } from '../../hooks/useWindowSizeFn'
import CodeMirror from 'codemirror'
import { MODE } from './type'
import { Nullable } from '../../types'
// css
import './codemirror.css'
import 'codemirror/theme/idea.css'
import 'codemirror/theme/material-palenight.css'
// modes
import 'codemirror/mode/javascript/javascript'
import 'codemirror/mode/css/css'
import 'codemirror/mode/htmlmixed/htmlmixed'

const props = defineProps({
    mode: {
        type: String as PropType<MODE>,
        default: MODE.JSON,
        validator(value: any) {
            // 这个值必须匹配下列字符串中的一个
            return Object.values(MODE).includes(value)
        }
    },
    value: { type: String, default: '' },
    readonly: { type: Boolean, default: false },
    height: {
        type: Number, default: 300
    }
})

const emit = defineEmits(['change'])

const el = ref()
let editor: Nullable<CodeMirror.Editor>

const debounceRefresh = useDebounceFn(refresh, 100)

watch(
    () => props.value,
    async (value) => {
        await nextTick();
        const oldValue = editor?.getValue()
        if (value !== oldValue) {
            editor?.setValue(value ? value : '')
        }
    },
    { flush: 'post' }
)

watchEffect(() => {
    editor?.setOption('mode', props.mode)
})

function setTheme() {
    unref(editor)?.setOption(
        'theme',
        'material-palenight'
    );
}

function refresh() {
    editor?.refresh()
}

async function init() {
    const addonOptions = {
        autoCloseBrackets: true,
        autoCloseTags: true,
        foldGutter: true,
        gutters: ['CodeMirror-linenumbers']
    }

    editor = CodeMirror(el.value!, {
        value: '',
        mode: props.mode,
        readOnly: props.readonly,
        tabSize: 2,
        theme: 'material-palenight',
        lineWrapping: true,
        lineNumbers: true,
        ...addonOptions
    });
    editor?.setValue(props.value)
    setTheme()
    editor?.on('change', () => {
        emit('change', editor?.getValue())
    })
}

onMounted(async () => {
    await nextTick()
    init()
    useWindowSizeFn<any>(debounceRefresh)
})

onUnmounted(() => {
    editor = null
})
</script>
